from fastapi import APIRouter, Body, HTTPException, WebSocket, WebSocketDisconnect
from typing import List, Optional, Dict, Any, AsyncGenerator, Tuple
import uuid
import json
from pydantic import BaseModel, Field
from app.core import factories, db_connect
import asyncio

import re
import tempfile
import subprocess
import os

from datetime import datetime

router = APIRouter()
interview_sessions = {}

db = db_connect.DatabaseConnection()


class JobPosition(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    name: str
    description: str


class Personality(BaseModel):
    personality_id: int
    name: str
    tone_style: str


class InterviewRequest(BaseModel):
    conversation_id: Optional[str] = None
    job_position_id: Optional[str] = None
    personality_id: Optional[int] = None
    user_input: Optional[str] = None


class InterviewResponse(BaseModel):
    message: str
    conversation_id: str
    follow_up_questions: Optional[List[str]] = None
    personalities: Optional[List[Personality]] = None


# 新增代码评估模型
class CodeEvaluationResult(BaseModel):
    syntax_valid: bool = True
    functionality_passed: bool = False
    style_issues: List[str] = [],
    feedback: str = ""
    test_results: List[Dict] = [],


class CodeSubmission(BaseModel):
    conversation_id: str
    code: str
    language: str  # "python" 或 "java"

class InterviewRecord(BaseModel):
    interview_id: int
    conversation_id: str
    job_position_id: str
    personality_id: int
    start_time: datetime
    end_time: Optional[datetime] = None

class InterviewMessage(BaseModel):
    message_id: int
    interview_id: int
    role: str
    content: str
    created_at: datetime
    sequence_number: int


# 保存新面试记录
def create_interview_record(conversation_id: str, job_position_id: str, personality_id: int) -> int:
    try:
        with db.cursor_context() as cursor:
            cursor.execute(
                """
                INSERT INTO interviews 
                (conversation_id, job_position_id, personality_id, start_time)
                VALUES (%s, %s, %s, NOW())
                """,
                (conversation_id, job_position_id, personality_id)
            )
            return cursor.lastrowid
    except Exception as e:
        print(f"创建面试记录失败: {e}")
        raise

# 保存面试消息
def save_interview_message(interview_id: int, role: str, content: str, sequence_number: int):
    try:
        with db.cursor_context() as cursor:
            cursor.execute(
                """
                INSERT INTO interview_messages 
                (interview_id, role, content, sequence_number, created_at)
                VALUES (%s, %s, %s, %s, NOW())
                """,
                (interview_id, role, content, sequence_number)
            )
    except Exception as e:
        print(f"保存面试消息失败: {e}")
        raise

# 更新面试结束时间
def update_interview_end_time(conversation_id: str):
    try:
        with db.cursor_context() as cursor:
            cursor.execute(
                """
                UPDATE interviews 
                SET end_time = NOW() 
                WHERE conversation_id = %s
                """,
                (conversation_id,)
            )
    except Exception as e:
        print(f"更新面试结束时间失败: {e}")
        raise

# 获取所有面试记录（用于下拉框）
def get_all_interview_records() -> List[InterviewRecord]:
    try:
        with db.cursor_context() as cursor:
            cursor.execute("""
                SELECT interview_id, conversation_id, job_position_id, personality_id, start_time, end_time
                FROM interviews
                ORDER BY start_time DESC
            """)
            return [InterviewRecord(**row) for row in cursor.fetchall()]
    except Exception as e:
        print(f"获取面试记录失败: {e}")
        return []

# 根据conversation_id获取面试消息
def get_interview_messages(conversation_id: str) -> List[InterviewMessage]:
    try:
        with db.cursor_context() as cursor:
            cursor.execute("""
                SELECT m.* 
                FROM interview_messages m
                JOIN interviews i ON m.interview_id = i.interview_id
                WHERE i.conversation_id = %s
                ORDER BY m.sequence_number ASC
            """, (conversation_id,))
            return [InterviewMessage(** row) for row in cursor.fetchall()]
    except Exception as e:
        print(f"获取面试消息失败: {e}")
        return []

def get_interview_id_by_conversation_id(conversation_id: str) -> Optional[int]:
    try:
        with db.cursor_context() as cursor:
            cursor.execute(
                """
                SELECT interview_id 
                FROM interviews 
                WHERE conversation_id = %s
                """,
                (conversation_id,)
            )
            result = cursor.fetchone()
            return result['interview_id'] if result else None
    except Exception as e:
        print(f"通过conversation_id获取interview_id失败: {e}")
        return None

# 代码评估工具函数
def evaluate_python_code(code: str, test_cases: List[str]) -> CodeEvaluationResult:
    result = CodeEvaluationResult()

    # 语法检查
    try:
        compile(code, "<string>", "exec")
    except SyntaxError as e:
        result.syntax_valid = False
        result.feedback = f"语法错误：{str(e)}"
        return result

    # 功能测试
    test_results = []
    passed = True
    with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
        f.write(code + "\n\n")
        for i, test in enumerate(test_cases):
            f.write(f"try:\n")
            f.write(f"    {test}\n")
            f.write(f"    print(f'TEST_{i}_PASSED')\n")
            f.write(f"except AssertionError:\n")
            f.write(f"    print(f'TEST_{i}_FAILED')\n")
        temp_name = f.name

    try:
        output = subprocess.check_output(
            ["python", temp_name],
            stderr=subprocess.STDOUT,
            timeout=5,
            text=True
        )
        for i in range(len(test_cases)):
            if f"TEST_{i}_PASSED" in output:
                test_results.append({"case": test_cases[i], "passed": True})
            else:
                test_results.append({"case": test_cases[i], "passed": False})
                passed = False
        result.functionality_passed = passed
        result.test_results = test_results
    except subprocess.TimeoutExpired:
        result.feedback = "代码执行超时（超过5秒）"
    except subprocess.CalledProcessError as e:
        result.feedback = f"执行错误：{e.output}"
    finally:
        os.unlink(temp_name)

    # 代码规范检查
    style_issues = []
    if len(code.splitlines()) > 50:
        style_issues.append("代码过长，建议拆分功能")
    if not re.search(r"def .*\(.*\):", code):
        style_issues.append("请使用函数封装功能")
    result.style_issues = style_issues

    # 生成反馈
    if result.functionality_passed:
        result.feedback = "代码功能测试通过！"
    else:
        result.feedback = "部分测试用例未通过，请检查代码逻辑"
    return result


def extract_test_cases(question: str) -> List[str]:
    """从问题描述中提取测试用例"""
    match = re.search(r"【测试用例】(.*)", question, re.DOTALL)
    if not match:
        return []
    return [line.strip() for line in match.group(1).splitlines() if line.strip()]


def detect_code_question(question: str) -> Tuple[bool, str]:
    """检测是否为代码题并返回语言类型"""
    question_lower = question.lower()
    if "python" in question_lower:
        return True, "python"
    if "java" in question_lower:
        return True, "java"
    return False, ""

def generate_code_question(job_position: str) -> Tuple[str, List[str]]:
    """根据岗位生成代码题和测试用例"""
    # 示例：根据不同岗位生成不同难度的题目
    if "python" in job_position.lower() or "后端" in job_position.lower():
        question = """请用Python实现一个函数，要求：
1. 函数名为fibonacci
2. 接收一个整数n作为参数
3. 返回第n个斐波那契数（n从0开始）
4. 需考虑n为0和负数的情况

【测试用例】
assert fibonacci(0) == 0
assert fibonacci(1) == 1
assert fibonacci(5) == 5
assert fibonacci(10) == 55
"""
        test_cases = [
            "assert fibonacci(0) == 0",
            "assert fibonacci(1) == 1",
            "assert fibonacci(5) == 5",
            "assert fibonacci(10) == 55"
        ]
        return question, test_cases

    elif "java" in job_position.lower() or "开发" in job_position.lower():
        question = """请用Java实现一个类，要求：
1. 类名为Calculator
2. 包含一个静态方法add，接收两个整数参数，返回它们的和
3. 包含一个静态方法multiply，接收两个整数参数，返回它们的积

【测试用例】
assert Calculator.add(2, 3) == 5
assert Calculator.multiply(4, 5) == 20
"""
        test_cases = [
            "assert Calculator.add(2, 3) == 5",
            "assert Calculator.multiply(4, 5) == 20"
        ]
        return question, test_cases

    # 默认题目
    return "请用Python实现一个求1到n的和的函数sum_to_n", ["assert sum_to_n(10) == 55"]


# 修改stream_response_generator函数，添加代码题检测和评估
async def stream_response_generator(chain, job_desc, user_input, chat_history, session) -> AsyncGenerator[str, None]:
    """增强版流式响应生成器，支持代码题处理"""
    # 检查是否需要生成代码题（对话轮次在3-5轮时）
    history_len = len(session["history"])
    is_code_question_round = 3 <= history_len <= 5 and not any("【测试用例】" in h["content"] for h in session["history"])

    if is_code_question_round:
        # 生成代码题
        code_question, test_cases = generate_code_question(job_desc)
        session["current_test_cases"] = test_cases  # 保存测试用例到会话
        full_response = f"现在我们来进行一个技术测试：\n{code_question}"
    else:
        # 检查是否是代码提交
        is_code_submission = any("【测试用例】" in h["content"] for h in session["history"]) and \
                             (user_input.strip().startswith("```") or "def " in user_input or "class " in user_input)

        if is_code_submission and "current_test_cases" in session:
            # 提取代码内容（移除可能的代码块标记）
            code = re.sub(r"```(python|java)?", "", user_input).strip()
            # 评估代码
            test_cases = session["current_test_cases"]
            language = "python" if "python" in job_desc.lower() else "java"

            if language == "python":
                result = evaluate_python_code(code, test_cases)

            # 生成评估反馈
            feedback = f"代码评估结果：\n"
            feedback += f"- 语法正确性：{'通过' if result.syntax_valid else '未通过'}\n"
            feedback += f"- 功能测试：{'全部通过' if result.functionality_passed else '部分通过'}\n"
            if result.style_issues:
                feedback += f"- 代码规范问题：{', '.join(result.style_issues)}\n"
            feedback += f"\n详细反馈：{result.feedback}\n\n接下来我们继续面试..."

            full_response = feedback
            del session["current_test_cases"]  # 清除测试用例
        else:
            # 正常对话生成
            full_response = chain.run(
                job_description=job_desc,
                user_input=user_input,
                chat_history=chat_history
            )

    # 流式输出
    for char in full_response:
        yield char
        await asyncio.sleep(0.03)


#------------------------------------------------------------------------

# 从数据库获取可用岗位
def get_available_positions_from_db():
    try:
        with db.cursor_context() as cursor:
            cursor.execute("SELECT title, company_type, description FROM jobpositions")
            return [
                JobPosition(id=row['title'], name=row['company_type'], description=row['description'])
                for row in cursor.fetchall()
            ]
    except Exception as e:
        print(f"获取岗位失败: {e}")
        return []


# 从数据库获取所有可用人格
def get_available_personalities():
    try:
        with db.cursor_context() as cursor:
            cursor.execute("SELECT personality_id, name, tone_style FROM personalities")
            return [
                Personality(
                    personality_id=row['personality_id'],
                    name=row['name'],
                    tone_style=row['tone_style']
                ) for row in cursor.fetchall()
            ]
    except Exception as e:
        print(f"获取人格失败: {e}")
        return []


# 根据ID获取人格
def get_personality_by_id(personality_id: int) -> Optional[Personality]:
    try:
        with db.cursor_context() as cursor:
            cursor.execute(
                "SELECT personality_id, name, tone_style FROM personalities WHERE personality_id = %s",
                (personality_id,)
            )
            row = cursor.fetchone()
            if row:
                return Personality(
                    personality_id=row['personality_id'],
                    name=row['name'],
                    tone_style=row['tone_style']
                )
            return None
    except Exception as e:
        print(f"获取人格失败: {e}")
        return None


AVAILABLE_JOBS = get_available_positions_from_db()
AVAILABLE_PERSONALITIES = get_available_personalities()


# 获取默认人格 (ID=1)
def get_default_personality():
    default = get_personality_by_id(1)
    if default:
        return default

    return Personality(
        personality_id=1,
        name="默认面试官",
        tone_style="您是一位面试官。您不太注重专业水平和深度，比较喜欢和面试者随和地在各方面交流"
    )


def build_chat_history(history_records: list) -> str:
    return "\n".join(
        f"{record['role'].capitalize()}: {record['content']}"
        for record in history_records
    )


@router.get("/positions", response_model=List[JobPosition])
def get_available_positions():
    return AVAILABLE_JOBS


@router.get("/personalities", response_model=List[Personality])
def get_available_personalities_api():
    return AVAILABLE_PERSONALITIES


@router.post("/start", response_model=InterviewResponse)
def start_or_continue_interview(request: InterviewRequest = Body(...)):
    conv_id = request.conversation_id

    default_personality = get_default_personality()

    if not conv_id:
        conv_id = str(uuid.uuid4())
        interview_sessions[conv_id] = {
            "job_position": None,
            "interview_chain": None,
            "history": [],
            "personality": default_personality
        }

    session = interview_sessions[conv_id]

    # 人格选择逻辑
    if request.personality_id is not None:
        selected_personality = get_personality_by_id(request.personality_id)
        if not selected_personality:
            raise HTTPException(
                status_code=404,
                detail=f"未找到ID为 {request.personality_id} 的面试官人格"
            )

        session["personality"] = selected_personality
        print(f"已切换面试官人格: {selected_personality.name} (ID: {selected_personality.personality_id})")

    # 岗位选择逻辑
    if request.job_position_id:
        selected_job = next((job for job in AVAILABLE_JOBS if job.id == request.job_position_id), None)
        if not selected_job:
            raise HTTPException(status_code=404, detail="岗位未找到")

        session["job_position"] = selected_job
        session["interview_chain"] = factories.get_interview_chain(
            selected_job,
            persona=session["personality"].tone_style
        )

        user_in = request.user_input or "让我们开始面试吧"
        session["history"].append({"role": "user", "content": user_in})

        chat_history_str = build_chat_history(session["history"][:-1])

        first_question = session["interview_chain"].run(
            job_description=selected_job.description,
            user_input=user_in,
            chat_history=chat_history_str
        )

        session["history"].append({"role": "ai", "content": first_question})

        return InterviewResponse(
            message=first_question,
            conversation_id=conv_id
        )

    # 用户回答问题逻辑
    elif request.user_input:
        if not session.get("job_position"):
            raise HTTPException(status_code=400, detail="请先选择面试岗位")

        user_answer = request.user_input
        session["history"].append({"role": "user", "content": user_answer})

        chat_history_str = build_chat_history(session["history"][:-1])

        follow_up_question = session["interview_chain"].run(
            job_description=session["job_position"].description,
            user_input=user_answer,
            chat_history=chat_history_str
        )

        session["history"].append({"role": "ai", "content": follow_up_question})

        return InterviewResponse(
            message=follow_up_question,
            conversation_id=conv_id,
            follow_up_questions=[follow_up_question]
        )

    # 初始请求（返回岗位和人格列表）
    else:
        job_list = "\n".join([f"{job.id} - {job.name}" for job in AVAILABLE_JOBS])
        personality_list = "\n".join(
            [f"{p.personality_id} - {p.name}" for p in AVAILABLE_PERSONALITIES]
        )

        return InterviewResponse(
            message=f"请选择岗位:\n{job_list}\n\n请选择面试官类型(输入ID):\n{personality_list}",
            conversation_id=conv_id,
            personalities=AVAILABLE_PERSONALITIES
        )


async def stream_response_generator(chain, job_desc, user_input, chat_history) -> AsyncGenerator[str, None]:
    """生成流式响应的异步生成器"""
    full_response = chain.run(
        job_description=job_desc,
        user_input=user_input,
        chat_history=chat_history
    )

    for char in full_response:
        yield char
        await asyncio.sleep(0.03)  # 控制字符输出速度，约30ms/字符


@router.websocket("/ws/{conversation_id}")
async def websocket_endpoint(websocket: WebSocket, conversation_id: str):
    await websocket.accept()

    # 验证会话是否存在
    if conversation_id not in interview_sessions:
        await websocket.send_text(json.dumps({
            "error": "会话不存在"
        }))
        await websocket.close()
        return

    session = interview_sessions[conversation_id]

    try:
        while True:
            # 接收客户端消息
            data = await websocket.receive_text()
            data = json.loads(data)
            user_input = data.get("user_input", "")

            if not session.get("job_position"):
                await websocket.send_text(json.dumps({
                    "error": "请先选择面试岗位"
                }))
                continue

            # 添加用户输入到历史
            session["history"].append({"role": "user", "content": user_input})
            chat_history_str = build_chat_history(session["history"][:-1])

            # 保存用户输入到数据库
            history_len = len(session["history"])
            save_interview_message(
                get_interview_id_by_conversation_id(conversation_id),  # 需要实现这个函数
                "user",
                user_input,
                history_len - 1
            )

            # 生成并发送流式响应
            full_response = ""
            # 创建流式生成器
            stream = stream_response_generator(
                session["interview_chain"],
                session["job_position"].description,
                user_input,
                chat_history_str
            )

            # 逐个字符接收并发送
            async for char in stream:
                full_response += char
                await websocket.send_text(json.dumps({
                    "chunk": char,  # 每次发送一个字符
                    "done": False
                }))

            # 发送完成标志
            await websocket.send_text(json.dumps({
                "chunk": "",
                "done": True
            }))

            # 保存完整响应到历史
            session["history"].append({"role": "ai", "content": full_response})

            # 保存AI响应到数据库
            save_interview_message(
                get_interview_id_by_conversation_id(conversation_id),
                "ai",
                full_response,
                len(session["history"]) - 1
            )

    except WebSocketDisconnect:
        # 连接断开时更新结束时间
        update_interview_end_time(conversation_id)
        print(f"WebSocket连接断开: {conversation_id}")
    except Exception as e:
        print(f"WebSocket错误: {str(e)}")
        await websocket.send_text(json.dumps({
            "error": str(e)
        }))

#获取面试记录的 API
@router.get("/interview-records", response_model=List[InterviewRecord])
def get_interview_records():
    """获取所有面试记录"""
    return get_all_interview_records()

@router.get("/interview-records/{conversation_id}/messages", response_model=List[InterviewMessage])
def get_conversation_messages(conversation_id: str):
    """获取特定面试的所有消息"""
    return get_interview_messages(conversation_id)


@router.post("/init", response_model=Dict[str, str])
def init_new_interview(request: InterviewRequest = Body(...)):
    """初始化新对话，返回对话ID并准备好第一个问题"""
    conv_id = str(uuid.uuid4())
    default_personality = get_default_personality()

    # 查找选中的岗位
    selected_job = None
    if request.job_position_id:
        selected_job = next((job for job in AVAILABLE_JOBS if job.id == request.job_position_id), None)
        if not selected_job:
            raise HTTPException(status_code=404, detail="岗位未找到")
    else:
        raise HTTPException(status_code=400, detail="请选择面试岗位")

    # 查找选中的人格
    selected_personality = default_personality
    if request.personality_id is not None:
        found = get_personality_by_id(request.personality_id)
        if found:
            selected_personality = found

    # 创建面试链
    interview_chain = factories.get_interview_chain(
        selected_job,
        persona=selected_personality.tone_style
    )

    # 初始化对话历史并生成第一个问题
    history = []
    # 添加初始用户输入作为触发第一个问题的信号
    initial_user_input = "让我们开始面试吧"
    history.append({"role": "user", "content": initial_user_input})

    # 构建聊天历史字符串（排除最后一条，即刚添加的初始输入）
    chat_history_str = build_chat_history(history[:-1])

    # 生成第一个面试问题
    first_question = interview_chain.run(
        job_description=selected_job.description,
        user_input=initial_user_input,
        chat_history=chat_history_str
    )

    # 将AI的第一个问题添加到历史
    history.append({"role": "ai", "content": first_question})

    # 创建会话
    interview_sessions[conv_id] = {
        "job_position": selected_job,
        "interview_chain": interview_chain,
        "history": history,  # 保存完整对话历史
        "personality": selected_personality
    }

    # 创建面试记录
    interview_id = create_interview_record(
        conv_id,
        selected_job.id,
        selected_personality.personality_id
    )

    # 保存初始消息
    for i, msg in enumerate(history):
        save_interview_message(interview_id, msg["role"], msg["content"], i)

    return {"conversation_id": conv_id}


# 在文件顶部导入必要的模块
from fastapi import status


# 添加PersonalityCreate模型
class PersonalityCreate(BaseModel):
    name: str
    description: str
    tone_style: str


@router.post("/personalities", response_model=Personality, status_code=status.HTTP_201_CREATED)
def create_personality(personality: PersonalityCreate):
    try:
        with db.cursor_context() as cursor:
            # 检查名称冲突
            cursor.execute(
                "SELECT COUNT(*) as count FROM personalities WHERE name = %s",
                (personality.name,)
            )
            if cursor.fetchone()['count'] > 0:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="已经有同名面试官了，换个名字吧"
                )

            # 检查性格描述冲突
            cursor.execute(
                "SELECT COUNT(*) as count FROM personalities WHERE tone_style = %s",
                (personality.tone_style,)
            )
            if cursor.fetchone()['count'] > 0:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="已经有相似性格的面试官了，快去找他聊聊吧"
                )

            cursor.execute(
                """
                INSERT INTO personalities (name, description, tone_style)
                VALUES (%s, %s, %s)
                """,
                (personality.name, personality.description, personality.tone_style)
            )

            new_personality_id = cursor.lastrowid

            cursor.execute(
                "SELECT personality_id, name, tone_style FROM personalities WHERE personality_id = %s",
                (new_personality_id,)
            )
            new_personality = cursor.fetchone()

            # 更新内存中的人格列表
            global AVAILABLE_PERSONALITIES
            AVAILABLE_PERSONALITIES = get_available_personalities()

            return Personality(
                personality_id=new_personality['personality_id'],
                name=new_personality['name'],
                tone_style=new_personality['tone_style']
            )
    except HTTPException as http_exc:
        # 如果是HTTPException，直接抛出
        raise http_exc
    except Exception as e:
        print(f"保存人格失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="保存人格失败，请重试"
        )
